home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 October / EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso / Aminet / comm / tcp / GetURL_1_03.lha / GetURL-1.03.rexx < prev   
OS/2 REXX Batch file  |  1995-01-31  |  58KB  |  1,642 lines

  1. /*=======================================================================*/
  2. /*  File Name : 'geturl.rexx',  02-Jan-95                                */
  3. /*=======================================================================*/
  4. /* Script to download HTML systems across the network */
  5. /*
  6.     Written by James Burton (burton@cs.latrobe.edu.au)
  7.     02-Jan-95
  8.     
  9.     **NOTE: requires AmiTCP & TCP: device**
  10. */
  11.                          
  12. gv.version = 'v1.03'
  13. version_string = '$VER: GetURL.rexx'
  14.  
  15. /*=======================================================================*/
  16. /*  Global Variables                                                     */
  17. /*=======================================================================*/
  18.             
  19. /* Constants (huh?) */
  20. gv.cr               = '0d'x
  21. gv.lf               = '0a'x
  22. gv.crlf             = gv.cr||gv.lf
  23. gv.true             = 1
  24. gv.false            = 0
  25.  
  26. /* Proxy stuff */
  27. gv.http_proxy_host  = ''
  28. gv.http_proxy_port  = '80'         
  29. gv.ftp_proxy_host   = ''
  30. gv.ftp_proxy_port   = '80'
  31.  
  32. /* Root Url */
  33. gv.root_url         = ''
  34. gv.root_method      = ''
  35. gv.root_host        = ''
  36. gv.root_port        = ''
  37. gv.root_path        = ''
  38.                     
  39. /* Files */
  40. gv.input_file       = ''
  41. gv.output_file      = ''
  42. gv.agenda_file      = 't:agenda-'||tmpname()
  43. gv.prune_file       = 't:prune-'||tmpname()
  44. gv.header_file      = 't:header-'||tmpname()
  45. gv.failed_file      = 't:failed-'||tmpname()
  46.  
  47. /* Flags */
  48. gv.recursive_fetch  = gv.false
  49. gv.use_proxy_cache  = gv.true
  50. gv.retry_failed     = gv.false
  51. gv.use_match        = gv.false
  52. gv.save_headers     = gv.false  /* Change the default to true for Brian :-) */
  53. gv.use_modified_since = gv.false
  54. gv.use_associative  = gv.false
  55. gv.use_level_marker = gv.false
  56.  
  57. /* Misc */          
  58. gv.local_prefix     = 'file://localhost|Mosaic:'
  59. gv.match_host       = ''
  60. gv.match_path       = ''
  61. gv.match_url        = ''
  62. gv.user_address     = ''
  63. gv.number_iterations = ''
  64. gv.depth            = 0                     /* bt */
  65. gv.max_depth        = 20                    /* bt */
  66. gv.max_length       = ''                    /* bt */
  67. gv.timezone         = '+1100'
  68. gv.problem_address  = 'burton@latcs1.lat.oz.au'
  69. gv.delay            = 2
  70. gv.level_marker     = '-'
  71.  
  72. /* arrays to hold agenda items */
  73. /* use: 
  74.     agenda. defaults to 0
  75.     agenda.head contains index of 1st path yet-to-visit
  76.     agenda.tail contains index of last path yet-to-visit
  77.     agenda.N    contains Nth path yet-to-visit
  78.     agenda.pathname contains recursion-level of path-yet-to-visit
  79.     prune.tail  contains index of next visited-path
  80.     prune.N    contains name of a visited-path
  81.     prune.pathname  contains    non-zero
  82.     failed.tail contains index of next failed-path
  83.     failed.N    contains name of a failed path
  84.     failed.pathname  contains    non-zero
  85. */
  86. agenda.     = 0
  87. agenda.head = 1
  88. agenda.tail = 0
  89. prune.      = 0
  90. prune.tail  = 0
  91. failed.     = 0
  92. failed.tail = 0
  93.  
  94. /*=======================================================================*/
  95. /*  Main                                                                 */
  96. /*=======================================================================*/
  97.  
  98. Main: 
  99.     parse arg gv.root_url rest  
  100.     
  101.     if substr(gv.root_url,1,1) = '-' then do /* no URL */
  102.         rest = gv.root_url rest             
  103.         gv.root_url = ''
  104.     end
  105.  
  106.     signal ON break_c       /* install control-c handler */
  107.  
  108.     OpenLibrary('rexxsupport.library',gv.true) /* insist on using this library */
  109.     OpenLibrary('rexxarplib.library')       /* non essential library */
  110.                       
  111.     Configure()                             /* setup global variables */
  112.     
  113.     /* look for and parse arguments */
  114.     arglist = rest
  115.     do while arglist ~= ''
  116.         parse var arglist flagname arglist
  117.         uflagname = upper(flagname)
  118.         DoArguments(uflagname)
  119.     end
  120.                 
  121.     if gv.root_url ~= '' then
  122.       do              
  123.         if gv.input_file = '' then
  124.           do
  125.             /* parse Root URL */
  126.             parse value ParseUrl(gv.root_url) with ok','url','gv.root_method','gv.root_host','gv.root_port','gv.root_path
  127.             if ~ok then do
  128.                 say 'failed to parse initial URL:' gv.root_url
  129.                 EXIT 20
  130.             end
  131.             if gv.output_file = '' then
  132.                 outputfile = MakePathname(gv.root_host,gv.root_path)
  133.             else
  134.                 outputfile = gv.output_file
  135.             if ~AppendHTTP(gv.root_host,gv.root_port,gv.root_path,outputfile,gv.agenda_file,gv.root_url) then
  136.                 AddToFailed(gv.failed_file,gv.root_url)
  137.             else    
  138.               do
  139.                 if ~PruneUrl(gv.root_url) then
  140.                     AddToPruned(gv.prune_file,gv.root_url)    /* don't want to visit the root again */
  141.               end
  142.             if gv.recursive_fetch then
  143.                 DoAgenda(gv.agenda_file)
  144.           end
  145.         else    /* ignore URL, use input file */
  146.           do
  147.             if gv.output_file = '' then
  148.                 outputfile = 't:output-'tmpname()
  149.             else
  150.                 outputfile = gv.output_file
  151.             AppendFile(gv.input_file,outputfile,gv.agenda_file)
  152.             if gv.recursive_fetch then
  153.                 DoAgenda(gv.agenda_file)
  154.           end
  155.       end
  156.     else    /* no URL specified */
  157.       do 
  158.         if gv.input_file ~= '' then 
  159.           do
  160.             if gv.output_file = '' then
  161.                 outputfile = 't:output-'tmpname()
  162.             else
  163.                 outputfile = gv.output_file
  164.             AppendFile(gv.input_file,outputfile,gv.agenda_file)
  165.             if gv.recursive_fetch then
  166.                 DoAgenda(gv.agenda_file)
  167.           end
  168.         else
  169.           do 
  170.             /* no input file, no URL */
  171.             /* may be something in the agenda */
  172.             if gv.recursive_fetch then
  173.                 DoAgenda(gv.agenda_file)
  174.           end
  175.       end       
  176.       
  177.     if gv.recursive_fetch & gv.retry_failed then do
  178.         if gv.use_associative then                       
  179.             BTAddFileTo(agenda,gv.failed_file)
  180.         else
  181.             address command 'copy' gv.failed_file gv.agenda_file
  182.         address command 'delete >NIL:' gv.failed_file
  183.         say 'Retrying previously failed URLs...'
  184.         DoAgenda(gv.agenda_file)
  185.     end
  186.                 
  187.     if gv.use_associative then do
  188.         BTDumpAgendaToFile(gv.agenda_file)
  189.         BTDumpFailedToFile(gv.failed_file)
  190.     end
  191. EXIT
  192.                 
  193. /*=======================================================================*/
  194. /*  DoArguments                                                          */
  195. /*=======================================================================*/
  196.  
  197. DoArguments:                 
  198.     parse var uflagname
  199.         select       
  200.             when abbrev('-HELP',uflagname,1) then 
  201.                 printusage()
  202.             when abbrev('-RECURSIVE',uflagname,2) then 
  203.                 gv.recursive_fetch = gv.true
  204.             when abbrev('-HOST',uflagname,2) then 
  205.                 parse var arglist gv.match_host arglist
  206.             when abbrev('-PATH',uflagname,2) then 
  207.                 parse var arglist gv.match_path arglist
  208.             when abbrev('-URL',uflagname,2)  then 
  209.                 do 
  210.                     parse var arglist gv.match_url arglist
  211.                     parse var gv.match_url dummy'://'gv.match_host'/'gv.match_path
  212.                 end
  213.             when abbrev('-OUTPUT',uflagname,2) then 
  214.                 parse var arglist gv.output_file arglist
  215.             when abbrev('-NOPROXY',uflagname,3) then
  216.                 do
  217.                     gv.http_proxy_host = ''
  218.                     gv.ftp_proxy_host = ''
  219.                 end
  220.             when abbrev('-INPUT',uflagname,2) then
  221.                 do  
  222.                     if arglist = '' then
  223.                         gv.input_file = '-'
  224.                     else
  225.                       do
  226.                         parse var arglist gv.input_file arglist
  227.                         if substr(gv.input_file,1,1) = '-' then do
  228.                             /* put the argument back please */
  229.                             arglist = gv.input_file arglist
  230.                             gv.input_file = '-'    /* flag to use stdin */
  231.                         end
  232.                       end
  233.                 end
  234.             when abbrev('-VISITED',uflagname,2) then
  235.               do
  236.                 parse var arglist gv.prune_file arglist
  237.                 if gv.use_associative then
  238.                     BTAddFileTo(prune,gv.prune_file)
  239.               end
  240.             when abbrev('-UNVISITED',uflagname,2) then
  241.               do
  242.                 parse var arglist gv.agenda_file arglist
  243.                 if gv.use_associative then
  244.                     BTAddFileTo(agenda,gv.agenda_file)
  245.               end
  246.             when abbrev('-DIRECTORY',uflagname,2) then
  247.                 say 'DIRECTORY flag not implemented yet'
  248.             when abbrev('-UPDATE',uflagname,2) then
  249.                 say 'UPDATE flag not implemented yet'
  250.             when abbrev('-NOPROXYCACHE',uflagname,2) then
  251.                 gv.use_proxy_cache = gv.false
  252.             when abbrev('-PROBLEM',uflagname,2) then
  253.               do 
  254.                 writeln(stdout,'Start typing problem or bug report...<end with '.' on line by itself>')
  255.                 SendMail(stdin,gv.problem_address,'GetUrl.rexx Problem' gv.version)
  256.               end
  257.             when abbrev('-SAVEROOT',uflagname,2) then
  258.               do                              
  259.                 parse var arglist newdir arglist
  260.                 if (right(newdir,1) ~= ':') & (right(newdir,1) ~= '/') then
  261.                     newdir = newdir'/'
  262.                 parse var gv.local_prefix before'|'after
  263.                 gv.local_prefix = before'|'newdir
  264.               end
  265.             when abbrev('-LENGTH',uflagname,2) then     
  266.                 parse var arglist gv.max_length arglist 
  267.             when abbrev('-DEPTH',uflagname,2) then      
  268.               do
  269.                 parse var arglist gv.max_depth arglist  
  270.                 gv.use_level_marker = gv.true
  271.               end
  272.             when abbrev('-SAVEHEADERS',uflagname,2) then
  273.                 gv.save_headers = gv.true
  274.             when abbrev('-NUMBER',uflagname,2) then
  275.                 parse var arglist gv.number_iterations arglist
  276.             when abbrev('-NEWVERSION',uflagname,2) then
  277.               do 
  278.                 parse var arglist newfile arglist
  279.                 if newfile = '' then do
  280.                     writech(stdout,'Destination for new version? [t:GetURL.rexx] ')
  281.                     newfile = readln(stdin)
  282.                     if newfile = '' then
  283.                         newfile = 't:GetURL.rexx'
  284.                 end
  285.                 say 'downloading new script...'
  286.                 CALL AppendHTTP('www.cs.latrobe.edu.au','','~burton/Public/GetURL.rexx',newfile,gv.agenda_file,'') 
  287.                 say 'downloaded' newfile', fetching documentation...'
  288.                 p = lastpos('.',newfile)
  289.                 if p > 0 then
  290.                     newfile = left(newfile,p)
  291.                 newfile = newfile'.doc'
  292.                 CALL AppendHTTP('www.cs.latrobe.edu.au','','~burton/Public/GetURL.doc',newfile,gv.agenda_file,'') 
  293.                 say 'downloaded' newfile
  294.               end
  295.             when abbrev('-FAILED',uflagname,2) then
  296.               do
  297.                 parse var arglist gv.failed_file arglist
  298.                 if gv.use_associative then
  299.                     BTAddFileTo(failed,failed_file)
  300.               end
  301.             when abbrev('-RETRY',uflagname,2) then
  302.                 gv.retry_failed = gv.true
  303.             when abbrev('-PATTERNMATCHING',uflagname,2) then
  304.               do
  305.                     writech(stdout,'should I really download Match? This will allow the use of regular expression patterns. [y/n] ')
  306.                     line = readln(stdin)
  307.                     if upper(left(line,1)) = 'Y' then do
  308.                         say 'Downloading Match...'
  309.                         newfile = 't:Match'
  310.                         CALL AppendHTTP('www.cs.latrobe.edu.au','','~burton/Public/C/Match',newfile,gv.agenda_file,'')
  311.                         say 'Match is now in your T: directory'
  312.                         say 'it matches a pattern against a string and prints either yes ot no.'
  313.                         say 'e.g. Match #?.gif hello.gif --> yes'
  314.                         say '     Match #?.gif hello.lha --> no'     
  315.                         say 'please install this program somewhere in your shell path.'
  316.                     end
  317.               end
  318.             when abbrev('-IFMODIFIED',uflagname,2) then
  319.                 gv.use_modified_since = gv.true
  320.             when abbrev('-ASSOCIATIVE',uflagname,2) then
  321.                 gv.use_associative = gv.true
  322.             when abbrev('-DELAY',uflagname,2) then
  323.                 parse var arglist gv.delay arglist
  324.             otherwise 
  325.                 say 'unused argument :' flagname
  326.         end
  327. RETURN gv.true
  328.  
  329. /*=======================================================================*/
  330. /*  SearchLine                                                           */
  331. /*=======================================================================*/
  332.  
  333. SearchLine: Procedure expose gv. agenda.
  334.     parse arg buffer,prefix,agenda
  335.                                       
  336.     signal ON break_c 
  337.  
  338.     line = buffer                             
  339.     do while line ~= ''
  340.         parse var line before method '=' '"' link '"'line 
  341.         method = upper(strip(method))
  342.         if link ~= '' & (method = 'HREF' | method = 'SRC') then do
  343.             AddToAgenda(agenda,CompleteUrl(prefix,link))
  344.         end
  345.     end
  346. RETURN gv.true
  347.                                         
  348. /*=======================================================================*/
  349. /*  CompleteUrl                                                          */
  350. /*=======================================================================*/
  351.  
  352. CompleteUrl: Procedure expose gv.
  353.     parse arg prefix,link
  354.                                    
  355.     if prefix = '' then /* nothing else we can do */
  356.         RETURN link
  357.  
  358.     if left(link,6) = 'mailto' then
  359.         RETURN link
  360.  
  361.     parse var link method '://' rest
  362.     if rest ~= '' then /* is already complete */
  363.       do    
  364.         RETURN link
  365.       end
  366.     else
  367.       do
  368.         parse var prefix front '|' back
  369.         select
  370.             when left(link,1) = '/' then    /* absolute path */
  371.                 RETURN front||link
  372.             when left(link,1) = '#' then    /* internal path */
  373.                 RETURN front
  374.             otherwise                           /* probably starts with '../' */
  375.                 if (right(back,1) = '/') | (back = '') then
  376.                     RETURN front'/'back||link
  377.                 else
  378.                     RETURN front'/'back'/'link
  379.       end
  380. RETURN gv.true
  381.     
  382. /*=======================================================================*/
  383. /*  AddToAgenda                                                          */
  384. /*=======================================================================*/
  385.     
  386. AddToAgenda: Procedure expose gv. agenda.
  387.     parse arg agenda,item,nocheck
  388.         
  389.     if (gv.use_associative) & (nocheck = '') then
  390.         RETURN(BTAddToAgenda(item))
  391.                 
  392.     if nocheck = '' then do
  393.         if AgendaSearch(item,agenda) then
  394.             RETURN(gv.false)
  395.     end
  396.         
  397.     if open(listf,agenda,'A') then
  398.       do
  399.         writeln(listf,item)
  400.         close(listf)
  401.       end
  402.     else                   
  403.       do 
  404.         if open(listf,agenda,'Write') then
  405.           do                                  
  406.             writeln(listf,item)
  407.             close(listf)
  408.           end
  409.         else
  410.             say 'unable to add to agenda file:' agenda
  411.       end
  412. RETURN gv.true               
  413.  
  414. AddToPruned: Procedure expose gv. prune.
  415.     parse arg agenda,item
  416.         
  417.     if gv.use_associative then
  418.         RETURN(BTAddToPrune(item))
  419.  
  420.     AddToAgenda(agenda,item)
  421. RETURN gv.true
  422.                             
  423. AddToFailed: Procedure expose gv. failed.
  424.     parse arg agenda,item
  425.         
  426.     if gv.use_associative then
  427.         RETURN(BTAddToFailed(item))
  428.  
  429.     AddToAgenda(agenda,item)
  430. RETURN gv.true
  431.  
  432. /*=======================================================================*/
  433. /*  RemoveFromAgenda                                                     */
  434. /*=======================================================================*/
  435.  
  436. RemoveFromAgenda: Procedure expose gv.
  437.     parse arg agenda
  438.     
  439.     if gv.use_associative then
  440.         RETURN gv.true
  441.  
  442.     signal ON break_c 
  443.     tmpname = agenda||'.tmp'
  444.  
  445.     if open(listf,agenda,'Read') then do
  446.         item = readln(listf)          
  447.         if open(tmp,tmpname,'Write') then
  448.           do
  449.             do while ~eof(listf)
  450.                 line = readln(listf)
  451.                 if length(line) > 0 then
  452.                     writeln(tmp,line)
  453.             end
  454.             close(tmp)
  455.             close(listf)
  456.                           
  457.             address command 'delete >NIL:' agenda
  458.             address command 'rename' tmpname agenda
  459.           end
  460.         else
  461.           do 
  462.             close(listf)
  463.             say 'unable to remove item from agenda file:' agenda
  464.           end
  465.         /* ***** NOTE: listf has been closed ***** */
  466.     end
  467. RETURN gv.true         
  468.  
  469. /*=======================================================================*/
  470. /*  AgendaTop                                                            */
  471. /*=======================================================================*/
  472.     
  473. AgendaTop: Procedure expose gv. agenda.
  474.     parse arg agenda
  475.     
  476.     if gv.use_associative then
  477.         RETURN(BTAgendaTop())
  478.  
  479.     top = ''
  480.     if open(listf,agenda,'Read') then do
  481.         if eof(listf) then
  482.             top = ''
  483.         else
  484.             top = readln(listf)
  485.         close(listf)
  486.     end
  487. RETURN top
  488.                    
  489. /*=======================================================================*/
  490. /*  AgendaSearch                                                         */
  491. /*=======================================================================*/
  492.  
  493. AgendaSearch: Procedure expose gv.
  494.     parse arg target,file
  495.     
  496.     if open(listf,file,'Read') then do
  497.         do while ~eof(listf)
  498.             line = readln(listf)
  499.             if line = target then do
  500.                 close(listf)
  501.                 RETURN gv.true
  502.             end
  503.         end                
  504.         close(listf)
  505.     end
  506. RETURN gv.false
  507.  
  508. /*=======================================================================*/
  509. /*  PruneUrl                                                             */
  510. /*=======================================================================*/
  511.  
  512. PruneUrl: Procedure expose gv.
  513.     parse arg url
  514.  
  515.     seen_previously = gv.false
  516.     if exists(gv.prune_file) then do
  517.         if AgendaSearch(url,gv.prune_file) then
  518.             seen_previously = gv.true
  519.     end 
  520.  
  521. RETURN seen_previously
  522.  
  523. /*=======================================================================*/
  524. /*  DoAgenda                                                             */
  525. /*=======================================================================*/
  526.  
  527. DoAgenda: Procedure expose gv. agenda. failed. prune.
  528.     parse arg agenda
  529.     
  530.     signal ON break_c
  531.                     
  532.     count = 0   
  533.     url = AgendaTop(agenda)
  534.     do while url ~= ''
  535.         if left(url,1) = gv.level_marker then do
  536.             parse var url dummy gv.depth
  537.             RemoveFromAgenda(agenda)
  538.             url = AgendaTop(agenda)
  539.             iterate
  540.         end
  541.         parse value ParseUrl(url) with ok','url','method','host','port','path
  542.         select
  543.             when ~ok then 
  544.                 nop   
  545.             when (gv.number_iterations ~= '') & (count >= gv.number_iterations) then
  546.                 break
  547.             when (gv.depth >= gv.max_depth) then   
  548.                 break                              
  549.             when PruneUrl(url) then
  550.                 nop
  551.             when ~MatchHost(host,gv.match_host) then
  552.                 nop
  553.             when ~MatchPath(path,gv.match_path) then
  554.                 nop
  555.             when method = 'HTTP' then 
  556.               do
  557.                 count = count + 1            
  558.                 if gv.output_file = '' then
  559.                     fname = MakePathname(host,path)
  560.                 else
  561.                     fname = gv.output_file
  562.                 say '['right(count,4)']' url '->' fname
  563.                 if ~AppendHTTP(host,port,path,fname,agenda,url) then
  564.                     AddToFailed(gv.failed_file,url)
  565.                 else
  566.                     AddToPruned(gv.prune_file,url)
  567.               end        
  568.             when method = 'FILE' then
  569.               do               
  570.                 /* FILE is always on localhost. Any other host uses FTP */
  571.                 count = count + 1          
  572.                 say '['right(count,4)']' 'reading' url
  573.                 parse var gv.local_prefix before'|'after
  574.                 if (right(after,1) ~= ':') & (right(after,1) ~= '/') then
  575.                     after = after'/'
  576.                 if pos(':',path) = 0 then
  577.                     fname = after||path
  578.                 else
  579.                     fname = path
  580.                 if TEXTFILE_TYPE(fname) then
  581.                     AppendFile(fname,,agenda)
  582.                 AddToPruned(gv.prune_file,url)
  583.               end
  584.             otherwise
  585.                 nop
  586.         end  
  587.         RemoveFromAgenda(agenda)
  588.         address command 'wait' gv.delay
  589.         url = AgendaTop(agenda)
  590.     end
  591. RETURN gv.true       
  592.                 
  593. /*=======================================================================*/
  594. /*  MatchHost                                                            */
  595. /*=======================================================================*/
  596.  
  597. MatchHost: Procedure expose gv. 
  598.     parse arg hostname,pattern
  599.                             
  600.     if pattern = '' then
  601.         RETURN gv.true
  602.         
  603.     if ~gv.use_match then
  604.       do
  605.         host = hostname
  606.         patn = pattern
  607.         do while (host ~= '') & (patn ~= '')
  608.             parse var host hname '.' host
  609.             parse var patn pname '.' patn
  610.             if (hname ~= pname) & (pname ~= '*') then
  611.                 RETURN gv.false
  612.         end
  613.         if patn = '*' then
  614.             RETURN gv.true
  615.         if host ~= patn then
  616.             RETURN gv.false
  617.       end
  618.     else                                    
  619.       do
  620.         address command 'match >ENV:result' '"'pattern'"' '"'hostname'"'
  621.         if GetEnvVar('result') = 'yes' then
  622.             RET = gv.true
  623.         else
  624.             RET = gv.false
  625.         RETURN RET
  626.       end
  627. RETURN gv.true             
  628.  
  629. /*=======================================================================*/
  630. /*  MatchPath                                                            */
  631. /*=======================================================================*/
  632.  
  633. MatchPath: Procedure expose gv.
  634.     parse arg pathname,pattern
  635.     
  636.     if pattern = '' then
  637.         RETURN gv.true
  638.  
  639.     if ~gv.use_match then
  640.       do
  641.         path = pathname
  642.         patn = pattern
  643.         do while (path ~= '') & (patn ~= '')
  644.             parse var path dname '/' path
  645.             parse var patn pname '/' patn
  646.             if (dname ~= pname) & (pname ~= '*') then
  647.                 RETURN gv.false
  648.         end                 
  649.         if patn = '*' then
  650.             RETURN gv.true
  651.         if path ~= patn then
  652.             RETURN gv.false
  653.       end
  654.     else
  655.       do
  656.         address command 'match >ENV:result' '"'pattern'"' '"'pathname'"'
  657.         if GetEnvVar('result') = 'yes' then
  658.             RETURN gv.true
  659.         else
  660.             RETURN gv.false
  661.       end
  662. RETURN gv.true
  663.  
  664. /*=======================================================================*/
  665. /*  MakePathname                                                         */
  666. /*=======================================================================*/
  667.  
  668. MakePathname: Procedure expose gv.
  669.     parse arg host,path     
  670.             
  671.     parse var gv.local_prefix localhost'|'savepath
  672.     ret = savepath||host'/'path
  673.     if (right(path,1) = '/') | (path = '') then
  674.         ret = ret||'index.html'
  675. RETURN ret
  676.  
  677. /*=======================================================================*/
  678. /*  Break_C                                                              */
  679. /*=======================================================================*/
  680.  
  681. Break_C:
  682.     say '*** BREAK ***'
  683.     say 'Transfer aborted'
  684.     if gv.use_associative then do
  685.         BTDumpAgendaToFile(gv.agenda_file)
  686.         BTDumpFailedToFile(gv.failed_file)
  687.     end
  688. EXIT 20
  689.  
  690. /*=======================================================================*/
  691. /*  AppendHTTP                                                           */
  692. /*=======================================================================*/
  693.  
  694. AppendHTTP: Procedure expose gv. agenda. prune. failed.
  695.     parse arg host,port,path,filename,agenda,url
  696.     
  697.     signal ON break_c 
  698.  
  699.     cache_header = ''
  700.  
  701.     /* check that we can actually proceed */            
  702.     if ~showlist('H','TCP') then do
  703.         say 'TCP: device is not mounted'
  704.         RETURN gv.false
  705.     end
  706.             
  707.     if gv.output_file = '' then
  708.         output_mode = 'Write'
  709.     else
  710.       do 
  711.         if ~exists(gv.output_file) then
  712.             output_mode = 'Write'
  713.         else
  714.             output_mode = 'Append'
  715.       end
  716.             
  717.     if right(path,1) = '/' then
  718.         prefix_path = path
  719.     else
  720.       do
  721.         lastslash = lastpos('/',path)
  722.         if lastslash = 0 then
  723.             prefix_path = ''
  724.         else
  725.             prefix_path = left(path,lastslash)
  726.       end           
  727.  
  728.     /* determine correct socket to open (check for proxy) */
  729.     if gv.http_proxy_host ~= '' then 
  730.       do   
  731.         /* use proxy host */
  732.         socketname = 'tcp:'gv.http_proxy_host'/'gv.http_proxy_port
  733.         if port = '' then
  734.           do
  735.             get_command = 'GET http://'host'/'path
  736.             prefix = 'http://'host'|'prefix_path
  737.           end
  738.         else 
  739.           do
  740.             get_command = 'GET http://'host':'port'/'path
  741.             prefix = 'http://'host':'port'|'prefix_path
  742.           end
  743.       end
  744.     else      
  745.       do                       
  746.         /* no proxy, so can do away with method,host,port */
  747.         if port = '' then
  748.           do
  749.             socketname = 'tcp:'host'/80'
  750.             get_command = 'GET /'path
  751.             prefix = 'http://'host'|'prefix_path
  752.           end
  753.         else 
  754.           do
  755.             socketname = 'tcp:'host'/'port
  756.             get_command = 'GET /'path
  757.             prefix = 'http://'host':'port'|'prefix_path
  758.           end
  759.       end
  760.     
  761.     OK = gv.false
  762.     /* Open socket to host */
  763.     if open(socket,socketname,'A') then
  764.       do                           
  765.         /* send FullRequest command */
  766.         writech(socket,get_command 'HTTP/1.0'||gv.crlf)
  767.         writech(socket,'From:' gv.user_address||gv.crlf)
  768.         writech(socket,'User-Agent: GetUrl.rexx' gv.version 'by burton@cs.latrobe.edu.au'||gv.crlf)
  769.         writech(socket,'Accept: */*, text/plain, text/html'||gv.crlf)
  770.         if (gv.http_proxy_host ~= '') & (~gv.use_proxy_cache) then
  771.             writech(socket,'Pragma: no-cache'||gv.crlf)
  772.         if gv.use_modified_since & exists(filename) then do
  773.             parse value statef(filename) with type len blocks prot days mins ticks comment
  774. /*say '<'days'/'mins'>'*/
  775.             parse value ApplyTimeZone(days,mins) with days','mins
  776. /*say '<'days'/'mins'>'*/
  777.             hr = mins % 60
  778.             mn = mins // 60
  779.             sc = 0
  780.             /*dy = left(date('weekday',days),3)*/
  781.             dy = date('weekday',days)
  782.             parse value date('normal',days) with dd mmm yyyy
  783.             dmy = dd'-'mmm'-'right(yyyy,2)
  784.             dt = dy',' dmy right(hr,2,'0')':'right(mn,2,'0')':'right(sc,2,'0')
  785. /*say '{'dt'}'*/
  786.             writech(socket,'If-Modified-Since:' dt||gv.crlf)
  787.         end
  788.         writech(socket,gv.crlf)       
  789.  
  790.         /* read first line of response */
  791.         if ~eof(socket) then
  792.             checkline = readch(socket,8)
  793.                 
  794.         OK = gv.true
  795.         if checkline = 'HTTP/1.0' then
  796.           do
  797.             header = gv.false
  798.  
  799.             /* Deal with header */
  800.             line = readln(socket)
  801.             parse var line response_code rest
  802.             if response_code = '200' then
  803.               do
  804.                 OK = gv.true
  805.                 if gv.save_headers then
  806.                     hfile = filename'.HDF'
  807.                 else
  808.                     hfile = gv.header_file
  809.                 header = open(headf,hfile,'Write')
  810.               end
  811.             else
  812.                 OK = gv.false
  813.             
  814.             /* read off header returned */
  815.             do while (~eof(socket)) & (line ~= '') & (line ~= gv.cr) & (line ~= gv.crlf)
  816.                 line = readln(socket)
  817.                 if header then
  818.                     writeln(headf,line)
  819.                 if gv.max_length ~= '' then do
  820.                     if upper(left(line,16)) = 'CONTENT-LENGTH: ' then do
  821.                         parse UPPER var line 'CONTENT-LENGTH: 'size
  822.                         if size > gv.max_length then do
  823.                             OK = gv.false              
  824.                             response_code = 'file too large ('size' Bytes)'
  825.                         end
  826.                     end
  827.                 end
  828.             end     
  829.             
  830.             if header then
  831.                 close(headf)
  832.           end
  833.  
  834.         if OK then
  835.           do
  836.             CreateDirectories(filename)
  837.             if open(new,filename,output_mode) then
  838.               do
  839.                 if HTMLFILE_TYPE(filename) then do
  840.                     date_string = time('Civil') date('Normal')   
  841.                     original_link = '[<a href="'url'">Original</a>]'
  842.                     local_link = '[<a href="file://localhost/'filename'">Local copy</a>]'           
  843.                     author_string = 'Saved by GetURL.rexx by <a href="http://www.cs.latrobe.edu.au/~burton/">James Burton</a>'
  844.                     cache_header = '<h6>Date saved : 'date_string' 'original_link' 'local_link' 'author_string'</h6>'
  845.                     writeln(new,cache_header)
  846.                 end
  847.                 if checkline ~= 'HTTP/1.0' then
  848.                     writech(new,checkline)
  849.  
  850.                 /* byte by byte transfer of the file */
  851.                 do while ~eof(socket)
  852.                     buffer = readch(socket,4096)
  853.                     writech(new,buffer)
  854.                 end
  855.                 close(new)
  856.               end
  857.             else
  858.                 say 'unable to open file:' filename 'for writing'
  859.           end
  860.         else                          
  861.           do
  862.             say 'HTTP ERROR' response_code':' rest 
  863.             say 'COMMAND:' get_command
  864.             OK = gv.false
  865.           end
  866.         close(socket)
  867.       end
  868.     else
  869.       do
  870.         say 'unable to reach host:' host
  871.         say 'socket:' socketname
  872.         OK = gv.false
  873.       end
  874.     if OK then
  875.         SearchFile(filename,prefix,agenda,length(cache_header))
  876. RETURN OK
  877.             
  878. /*=======================================================================*/
  879. /*  ApplyTimeZone                                                        */
  880. /*=======================================================================*/
  881.  
  882. ApplyTimeZone: Procedure expose gv.
  883.     parse arg days,mins
  884.     
  885.     sign = left(gv.timezone,1)
  886.     offset = strip(substr(gv.timezone,2))
  887.     mn_offset = right(offset,2)
  888.     hr_offset = left(offset,length(offset)-2)
  889.     
  890.     hr = mins % 60
  891.     mn = mins // 60
  892.                           
  893.     if sign = '+' then
  894.       do /* subtract the offset */
  895.         if hr_offset > hr then do
  896.             days = days - 1
  897.             hr = hr + 24         
  898.         end
  899.         hr = hr - hr_offset
  900.  
  901.         if mn_offset > mn then do
  902.             hr = hr - 1
  903.             mn = mn + 60
  904.         end             
  905.         mn = mn - mn_offset
  906.       end
  907.     else
  908.       do    /* add the offset */
  909.         hr = hr + hr_offset
  910.         if hr > 24 then do
  911.             days = days + 1
  912.             hr = hr - 24
  913.         end             
  914.         
  915.         mn = mn + mn_offset
  916.         if mn > 60 then do
  917.             hr = hr + 1
  918.             mn = mn - 60
  919.         end
  920.       end
  921.       
  922.     mins = hr * 60 + mn
  923. RETURN days','mins
  924.  
  925. /*=======================================================================*/
  926. /*  SearchFile                                                           */
  927. /*=======================================================================*/
  928.  
  929. SearchFile: Procedure expose gv. agenda. prune.
  930.     parse arg filename,prefix,agenda,skip_length
  931.                                       
  932.     signal ON break_c
  933.  
  934.     /* now search it */
  935.     if HTMLFILE_TYPE(filename) then do
  936.         if open(new,filename,'Read') then do
  937.             if skip_length > 0 then
  938.                 readch(new,skip_length) /* skip the top of the file */
  939.             do while ~eof(new)
  940.                 buffer = readln(new)
  941.                 SearchLine(buffer,prefix,agenda)
  942.             end                   
  943.             if (gv.use_level_marker) & (~gv.use_associative) then
  944.                 AddToAgenda(agenda,gv.level_marker gv.depth+1,'force')
  945.             close(new)
  946.         end
  947.     end         
  948. RETURN gv.true
  949.  
  950. /*=======================================================================*/
  951. /*  CreateDirectories                                                    */
  952. /*=======================================================================*/
  953.  
  954. CreateDirectories: Procedure expose gv.
  955.     parse arg filename
  956.             
  957.     signal ON break_c
  958.  
  959.     parse var filename dev ':' path
  960.     current_dir = dev':'
  961.     do while path ~= ''
  962.         parse var path dir '/' path
  963.         if right(current_dir,1) = ':' then
  964.             current_dir = current_dir||dir
  965.         else
  966.             current_dir = current_dir'/'dir
  967.         if path ~= '' then 
  968.             if ~exists(current_dir) then
  969.                 address command 'makedir' current_dir
  970.     end
  971. RETURN gv.true
  972.  
  973. /*=======================================================================*/
  974. /*  HTMLFILE_TYPE                                                        */
  975. /*=======================================================================*/
  976.  
  977. HTMLFILE_TYPE: Procedure expose gv.
  978.     parse arg path
  979.                   
  980.     dot = lastpos('.',path)
  981.     if dot = 0 then
  982.       do 
  983.         if right(path,1) = '/' then
  984.             RETURN gv.true
  985.         else
  986.             RETURN gv.false
  987.       end
  988.     else
  989.       do 
  990.         extension = upper(substr(path,dot,4))
  991.         if extension = '.HTM' then
  992.             RETURN gv.true
  993.         else
  994.             RETURN gv.false
  995.       end
  996. RETURN gv.true
  997.  
  998. /*=======================================================================*/
  999. /*  TEXTFILE_TYPE                                                        */
  1000. /*=======================================================================*/
  1001.  
  1002. TEXTFILE_TYPE: Procedure expose gv.
  1003.     parse arg path
  1004.                   
  1005.     dot = lastpos('.',path)
  1006.     if dot = 0 then
  1007.       do 
  1008.         if right(path,1) = '/' then
  1009.             RETURN gv.true
  1010.         else
  1011.             RETURN gv.false
  1012.       end
  1013.     else
  1014.       do 
  1015.         extension = upper(substr(path,dot,4))
  1016.         if (extension = '.TXT') | (extension = '.DOC') | (extension = '.TEX') | (extension = '.HTM') then
  1017.             RETURN gv.true
  1018.         else
  1019.             RETURN gv.false
  1020.       end
  1021. RETURN gv.true
  1022.  
  1023. /*=======================================================================*/
  1024. /*  AppendFile                                                           */
  1025. /*=======================================================================*/
  1026.  
  1027. AppendFile: Procedure expose gv. agenda. prune.
  1028.     parse arg input,output,agenda
  1029.     
  1030.     signal ON break_c 
  1031.                    
  1032.     out = gv.false
  1033.     if output ~= '' then do
  1034.         out = open(outputfile,output,'Write')
  1035.         if ~out then
  1036.             say 'unable to open output file :' output
  1037.     end
  1038.  
  1039.     if input = '-' then
  1040.       do
  1041.         do while ~eof(stdin)
  1042.             buffer = readln(stdin)
  1043.             SearchLine(buffer,gv.local_prefix,agenda)
  1044.             if out then
  1045.                 writeln(outputfile,buffer)
  1046.         end
  1047.       end
  1048.     else 
  1049.       do
  1050.         if open(inputfile,input,'Read') then
  1051.           do                         
  1052.             do while ~eof(inputfile)
  1053.                 buffer = readln(inputfile)
  1054.                 SearchLine(buffer,gv.local_prefix,agenda)
  1055.                 if out then
  1056.                     writeln(outputfile,buffer)
  1057.             end
  1058.             close(inputfile)
  1059.           end   
  1060.         else
  1061.             say 'unable to open input file :' input
  1062.       end   
  1063.       
  1064.     if out then do
  1065.         close(outputfile)
  1066.     end
  1067. RETURN gv.true
  1068.  
  1069. /*=======================================================================*/
  1070. /*  PrintUsage                                                           */
  1071. /*=======================================================================*/
  1072.  
  1073. PrintUsage:
  1074.     say 'GetURL' gv.version ': Written by James Burton (burton@cs.latrobe.edu.au)'
  1075.     say 'USAGE  :'
  1076.     say '    rx geturl [<url>] [<options>]'
  1077.     say    
  1078.     say '<url> can be'
  1079.     say '    http:[//]host[:port]/[path]'
  1080.     say
  1081.     say '<options> can be'                                                    
  1082.     say '    -Help              display this message'
  1083.     say '    -Problem           send message to me, bug report, query etc.'
  1084.     say '    -NewVersion <file> downloads most recent version of GetURL.rexx'
  1085.     say '    -PatternMatching   downloads pattern matching utility'
  1086.     say '    -Associative       use associative array rather than files'
  1087.     say '    -Delay <num>       delay <num> seconds before next file'
  1088.     say
  1089.     say '    -Recursive         recursively collect files, following contained URLs'
  1090.     /*say '    -Directory <dir>   recursively collect URLs from text files in <dir>'*/
  1091.     /*say '    -Update <dir>      recursively check files in <dir> for updates'*/
  1092.     say '    -NoProxy           fetch directly from remote site, ignore proxy config'
  1093.     say '    -NoProxyCache      force proxy to reload cache'
  1094.     say '    -Retry             retry previously failed URLs (if -recursive set)'
  1095.     say
  1096.     say '    -Host <pattern>    only collect files from hosts matching <pattern>'
  1097.     say '    -Path <pattern>    only collect files from paths matching <pattern>'
  1098.     say '    -URL  <pattern>    only collect files from URLs matching <pattern>'
  1099.     say '    -Number <num>      only collect <num> files'
  1100.     say '    -Length <num>      only collect files smaller than <num> bytes'
  1101.     say '    -IfModified        only collect files modified since cached'
  1102.     say '    -Depth <num>       only collect files from <num> levels of recursion'
  1103.     say
  1104.     say '    -Input <filename>  read input from file instead of <url>'
  1105.     say '    -Input             read input from standard input instead of <url>'
  1106.     say '    -Output <filename> concatenate all files onto <filename>'
  1107.     say '    -SaveRoot <dir>    save files under <dir> rather than Mosaic:'
  1108.     say '    -Visited <file>    use specified visited file, won''t revisit these URLs'
  1109.     say '    -Unvisited <file>  use specified unvisited file, visit these URLs first'
  1110.     say '    -Failed <file>     use specified failed file, these ones did not transfer'
  1111.     say '    -SaveHeaders       save transfer header to *.HDF'
  1112.     say
  1113.     say '<options> can all be abbreviated.'
  1114.     say                                                                                  
  1115. RETURN gv.true
  1116.  
  1117. /*=======================================================================*/
  1118. /*  Configuration                                                        */
  1119. /*=======================================================================*/
  1120.          
  1121. Configure: Procedure expose gv. 
  1122.  
  1123.     /* look for HTTP Proxy server */
  1124.     if GetEnvVar('WWW_HTTP_GATEWAY') ~= '' then
  1125.         parse value GetEnvVar('WWW_HTTP_GATEWAY') with 'http://'gv.http_proxy_host':'gv.http_proxy_port'/'
  1126.     else if GetEnvVar('HTTP_PROXY') ~= '' then
  1127.         parse value GetEnvVar('HTTP_PROXY') with 'http://'gv.http_proxy_host':'gv.http_proxy_port'/'
  1128.     else
  1129.         gv.http_proxy_host = ''        
  1130.         
  1131.     /* look for FTP Proxy server */
  1132.     if GetEnvVar('WWW_FTP_GATEWAY') ~= '' then
  1133.         parse value GetEnvVar('WWW_FTP_GATEWAY') with 'http://'gv.ftp_proxy_host':'gv.ftp_proxy_port'/'
  1134.     else if GetEnvVar('FTP_PROXY') ~= '' then
  1135.         parse value GetEnvVar('FTP_PROXY') with 'http://'gv.ftp_proxy_host':'gv.ftp_proxy_port'/'
  1136.     else
  1137.         gv.ftp_proxy_host = ''        
  1138.     
  1139.     /* look for User's login name */
  1140.     if GetEnvVar('USER') ~= '' then
  1141.         user = GetEnvVar('USER')
  1142.     else
  1143.       do 
  1144.         if GetEnvVar('LOGNAME') ~= '' then
  1145.             user = GetEnvVar('LOGNAME')
  1146.         else
  1147.             user = GetEnvVar('USERNAME')
  1148.       end   
  1149.       
  1150.     /* look for User's real name */
  1151.     real = GetEnvVar('REALNAME')
  1152.     
  1153.     /* look for Machine's name */
  1154.     if GetEnvVar('HOSTNAME') ~= '' then
  1155.         host = GetEnvVar('HOSTNAME')
  1156.     else
  1157.       do 
  1158.         if GetEnvVar('HOST') ~= '' then
  1159.             host = GetEnvVar('HOST')
  1160.         else
  1161.             host = GetEnvVar('NODENAME')||GetEnvVar('DOMAINNAME')
  1162.       end                                                        
  1163.  
  1164.     /* construct email address */
  1165.     gv.user_address = real '<'user'@'host'>'
  1166.     
  1167.     /* look for match program */
  1168.     address command 'which >env:result match'
  1169.     if GetEnvVar('result') ~= '' then
  1170.       do 
  1171.         r = GetEnvVar('result')
  1172.         address command 'version >ENV:result' r 'full'
  1173.         r = GetEnvVar('result')
  1174.         r = compress(r,'0a'x)
  1175.         if r = 'Match 1.0 (16/01/95)(16-Jan-95) James Burton (burton@cs.latrobe.edu.au)' then
  1176.             gv.use_match = gv.true
  1177.         else
  1178.             gv.use_match = gv.false
  1179.       end
  1180.     else
  1181.         gv.use_match = gv.false
  1182.         
  1183.     /* make sure we have a valid timezone */
  1184.     if gv.timezone = '' then do
  1185.         say 'Since this is the first time you have run GetURL' gv.version
  1186.         say 'we need to know the Time Zone you are in relative to GMT (England time)'
  1187.         say 'Please enter the hrs & mins e.g. +11:00 (AEDT) or +10:30 (ACDT)'
  1188.         writech(stdout,'Enter TimeZone [+/-]<hr>:<mins> ')
  1189.         line = readln(stdin)
  1190.         parse value substr(line,2) with hr ':' mn
  1191.         sign = left(line,1)
  1192.         say 'TimeZone is' sign||hr':'mn
  1193.         gv.timezone = sign||hr||mn
  1194.         parse source dummy dummy dummy resolved_name dummy
  1195.         if open(f,resolved_name,'read') then
  1196.           do             
  1197.             t_tmpname = 't:tmp-'tmpname()
  1198.             if open(fout,t_tmpname,'Write') then
  1199.               do           
  1200.                 i = 0
  1201.                 do while ~eof(f)
  1202.                     line = readln(f)
  1203.                     if left(line,11) = 'gv.timezone' then
  1204.                       do
  1205.                         writeln(fout,'gv.timezone =' '"'gv.timezone'"')
  1206.                         writech(stdout,'I')
  1207.                       end
  1208.                     else
  1209.                       do
  1210.                         writeln(fout,line)
  1211.                         if (i // 30) = 0 then
  1212.                             writech(stdout,'.')
  1213.                       end
  1214.                     i = i + 1
  1215.                 end                     
  1216.                 writeln(stdout,'<<')
  1217.                 close(fout)                   
  1218.                 /*address command 'copy' t_tmpname resolved_name*/
  1219.                 address command 'copy' t_tmpname 't:GetURL-configured.rexx'
  1220.                 address command 'delete >NIL:' t_tmpname
  1221.                 say 'Please use t:GetURL-configured.rexx in future'
  1222.               end         
  1223.             else
  1224.                 say 'unable to open temp file'
  1225.             close(f)
  1226.           end
  1227.         else
  1228.             say 'unable to find the program file, please edit the timzone in by hand.'
  1229.     end
  1230. RETURN gv.true
  1231.  
  1232. /*=======================================================================*/
  1233. /*  OpenLibrary                                                          */
  1234. /*=======================================================================*/
  1235.  
  1236. OpenLibrary: Procedure expose gv.
  1237.     parse arg libname,strict
  1238.                        
  1239.     ret = gv.true
  1240.  
  1241.     if ~show('Libraries',libname) then do
  1242.         if ~addlib(libname,0,-30,0) then do
  1243.             ret = gv.false
  1244.             if strict then do
  1245.                 say 'unable to open ARexx library :' libname
  1246.                 EXIT 20
  1247.             end
  1248.         end
  1249.     end
  1250. RETURN ret
  1251.             
  1252. /*=======================================================================*/
  1253. /*  GetEnvVar                                                            */
  1254. /*=======================================================================*/
  1255.  
  1256. GlobalGetEnv: Procedure expose gv.
  1257.     parse arg env_variable
  1258.     
  1259.     if open(varfile,'ENV:'||env_variable,'Read') then
  1260.         do
  1261.             line = readln(varfile)
  1262.             close(varfile)
  1263.         end
  1264.     else
  1265.         line = ''
  1266. RETURN line      
  1267.  
  1268. GetEnvVar: Procedure expose gv.
  1269.     parse arg env_variable
  1270.                         
  1271.     if show('Libraries','rexxarplib.library') then
  1272.       do
  1273.         r = getenv(env_variable)
  1274.         if right(r,1) = '0a'x then
  1275.             r = left(r,length(r)-1)
  1276.         RETURN r
  1277.       end
  1278.     else
  1279.         RETURN GlobalGetEnv(env_variable)
  1280.     end
  1281. RETURN
  1282.  
  1283. /*=======================================================================*/
  1284. /*  ParseUrl                                                             */
  1285. /*=======================================================================*/
  1286.  
  1287. ParseUrl: Procedure expose gv.
  1288.     parse arg ret_url
  1289.       
  1290.     signal ON break_c 
  1291.     ok = gv.true
  1292.                                
  1293.     parse var ret_url ret_method':'rest
  1294.     ret_method = upper(ret_method)
  1295.     select
  1296.         when ret_method = 'HTTP' then
  1297.             do           
  1298.                 /* set defaults for HTTP */
  1299.                 ret_host = ''
  1300.                 ret_port = 80
  1301.                 ret_path = ''
  1302.  
  1303.                 parse var rest '//'host_part'/'ret_path
  1304.                 if host_part = '' then  /* allow missing '//' */
  1305.                     parse var rest host_part'/'ret_path
  1306.                 parse var host_part ret_host':'ret_port
  1307.                 parse var ret_path ret_path '#' dummy   /* these can be safely ignored */
  1308.                 if pos('?',ret_path) = 0 then
  1309.                     ok = gv.true
  1310.                 else
  1311.                     ok = gv.false
  1312.             end
  1313.         when ret_method = 'FILE' then
  1314.             do  
  1315.                 /* set defaults for FILE */
  1316.                 ret_host = ''
  1317.                 ret_port = ''
  1318.                 ret_path = ''
  1319.                 
  1320.                 parse var rest '//'host_part'/'ret_path
  1321.                 if host_part = '' then
  1322.                     parse var rest host_part'/'ret_path
  1323.                 parse var host_part ret_host':'ret_port
  1324.                 
  1325.                 if (ret_host = 'localhost') | (ret_host = '') then
  1326.                     ret_host = 'localhost'
  1327.                 else
  1328.                     ret_method = 'FTP'
  1329.                 ok = gv.true          
  1330.             end
  1331.         when ret_method = 'FTP' then
  1332.             do 
  1333.                 say 'FTP unimplemented'
  1334.                 ok = gv.false
  1335.             end
  1336.         when ret_method = 'MAILTO' then
  1337.             do 
  1338.                 say 'MAILTO unimplemented'
  1339.                 ok = gv.false
  1340.             end
  1341.         when ret_method = 'TELNET' then
  1342.             do 
  1343.                 say 'TELNET unimplemented'
  1344.                 ok = gv.false
  1345.             end
  1346.         when ret_method = 'NEWS' then
  1347.             do 
  1348.                 say 'NEWS unimplemented'
  1349.                 ok = gv.false
  1350.             end
  1351.         when ret_method = 'WAIS' then
  1352.             do 
  1353.                 say 'WAIS unimplemented'
  1354.                 ok = gv.false
  1355.             end 
  1356.         when ret_method = 'GOPHER' then
  1357.             do 
  1358.                 say 'GOPHER unimplemented'
  1359.                 ok = gv.false
  1360.             end
  1361.         otherwise 
  1362.             do      
  1363.                 /* not much more we can do if do not recognise the access method */
  1364.                 say 'unknown HTTP Method :' ret_method
  1365.                 ok = gv.false
  1366.             end
  1367.     end              
  1368. RETURN ok','ret_url','ret_method','ret_host','ret_port','ret_path
  1369.  
  1370. /*=======================================================================*/
  1371. /*  TmpName                                                              */
  1372. /*=======================================================================*/
  1373.  
  1374. TmpName:
  1375. RETURN date(days)"-"time(seconds)
  1376.                 
  1377. /*=======================================================================*/
  1378. /*  SendMail                                                             */
  1379. /*=======================================================================*/
  1380.  
  1381. SendMail: Procedure expose gv.
  1382.     parse arg openfile,to_address,subject
  1383.                       
  1384.     parse var to_address to_login'@'to_host
  1385.     parse var gv.user_address real'<'from_login'@'from_host'>'
  1386.     socketname = 'tcp:'to_host'/smtp'
  1387.     end_mail = '2e'x
  1388.     send_timestamp = left(date('weekday'),3) date('normal') time('normal')
  1389.     message_id = tmpname()'@'from_host
  1390.  
  1391.     if open(socket,socketname,'Write') then
  1392.       do
  1393.         if ~CheckMailResult(socket) then call SendMailQuit
  1394.         writech(socket,'HELO' from_host||gv.crlf)
  1395.         if ~CheckMailResult(socket) then call SendMailQuit
  1396.         writech(socket,'MAIL FROM:' '<'from_login'@'from_host'>'||gv.crlf)
  1397.         if ~CheckMailResult(socket) then call SendMailQuit
  1398.         writech(socket,'RCPT TO:' '<'to_address'>'||gv.crlf)
  1399.         if ~CheckMailResult(socket) then call SendMailQuit
  1400.         writech(socket,'DATA'||gv.crlf)
  1401.         if ~CheckMailResult(socket) then call SendMailQuit
  1402.         writech(socket,'From' from_login'@'from_host send_timestamp||gv.crlf)
  1403.         writech(socket,'Received: by' from_host '(GetUrl.rexx' gv.version 'burton@cs.latrobe.edu.au)'||gv.crlf)
  1404.         writech(socket,'Message-Id:' '<'message_id'>'||gv.crlf)
  1405.         writech(socket,'Date:' send_timestamp '+0000'||gv.crlf)
  1406.         writech(socket,'From:' gv.user_address||gv.crlf)
  1407.         writech(socket,'To:' to_address||gv.crlf)
  1408.         writech(socket,'Subject:' subject||gv.crlf)
  1409.         writech(socket,'X-Mailer: GetURL.rexx' gv.version 'by James Burton (burton@cs.latrobe.edu.au)'||gv.crlf)
  1410.         writech(socket,gv.crlf)
  1411.         address command 'wait 1'
  1412.         do while ~eof(openfile)
  1413.             line = readln(openfile)
  1414.             if line = '.' then 
  1415.                 break
  1416.             writech(socket,line||gv.crlf)
  1417.         end                           
  1418.         writech(socket,end_mail||gv.crlf)
  1419.         if CheckMailResult(socket) then
  1420.             say 'Message sent OK.'
  1421. SendMailQuit:
  1422.         writech(socket,'QUIT'||gv.crlf)
  1423.         close(socket)
  1424.       end
  1425.     else
  1426.         say 'could not open SMTP socket to host:' to_host
  1427. RETURN gv.true     
  1428.  
  1429. CheckMailResult: Procedure expose gv.
  1430.     parse arg socket
  1431.     
  1432.     do while ~eof(socket)
  1433.         line = readln(socket)
  1434. /*say '"'line'"'*/
  1435.         if substr(line,4,1) ~= '-' then
  1436.             break
  1437.     end          
  1438.     status_code = left(line,3)
  1439. /*say '['status_code']'*/
  1440.     if status_code > 399 then
  1441.         RETURN gv.false
  1442.     else
  1443.         RETURN gv.true
  1444. RETURN gv.true
  1445.  
  1446. /*=======================================================================*/
  1447. /*  AddToFailed                                                          */
  1448. /*      adds item to next failed                                         */
  1449. /*=======================================================================*/
  1450.  
  1451. BTAddToFailed: Procedure expose gv. failed.
  1452.     parse arg item
  1453.     
  1454. say 'AddToFailed ' item
  1455.     if failed.item = 0 then     /* if not already known */
  1456.     do
  1457. say 'accepted'
  1458.         next = failed.tail + 1  /* index for this item */
  1459.         failed.tail = next              /* advance tail of failed */
  1460.         failed.next = item              /* store item at tail */
  1461.         failed.item = 1
  1462.     end
  1463.  
  1464. RETURN gv.true               
  1465.  
  1466. /*=======================================================================*/
  1467. /*  AddToPrune                                                          */
  1468. /*      adds item to next prune                                         */
  1469. /*=======================================================================*/
  1470.  
  1471. BTAddToPrune: Procedure expose gv. prune.
  1472.     parse arg item
  1473.     
  1474. say 'AddToPrune ' item
  1475.     if prune.item = 0 then      /* if not already known */
  1476.     do
  1477. say 'accepted'
  1478.         next = prune.tail + 1   /* index for this item */
  1479.         prune.tail = next               /* advance tail of prune */
  1480.         prune.next = item               /* store item at tail */
  1481.         prune.item = 1
  1482.     end
  1483.  
  1484. RETURN gv.true               
  1485.  
  1486. /*=======================================================================*/
  1487. /*  AddToAgenda                                                          */
  1488. /*      adds item to tail of agenda                                      */
  1489. /*=======================================================================*/
  1490.  
  1491. BTAddToAgenda: Procedure expose gv. agenda.
  1492.     parse arg item
  1493.     
  1494. say 'AddToAgenda' item
  1495. say agenda.item
  1496. if (agenda.item ~= 0) then say 'already known'
  1497. if (index(item,'#') ~= 0) then say 'is a # reference'
  1498.     /* if not already known  and not an internal reference */
  1499.     if (agenda.item = 0) & (index(item,'#') = 0)  then  
  1500.     do
  1501. say 'accepted'
  1502.         next_depth = gv.depth + 1  /* next recursion level */
  1503.         next = agenda.tail + 1  /* index for this item */
  1504.         agenda.tail = next              /* advance tail of agenda */
  1505.         agenda.next = item              /* store item at tail */
  1506.         agenda.item = next_depth        /* and store depth at item */
  1507.     end
  1508.  
  1509. RETURN gv.true               
  1510.  
  1511. /*=======================================================================*/
  1512. /*  AgendaTop                                                            */
  1513. /*     return the topmost (first) item in the agenda                     */
  1514. /*     and remove it from the agenda                                     */
  1515. /*=======================================================================*/
  1516.  
  1517. BTAgendaTop: Procedure expose gv. agenda.
  1518.     
  1519. say 'AgendaTop'
  1520.     next = agenda.head          /* location of next item */
  1521. say 'next ' next
  1522.     item = agenda.next          /* get it */
  1523. say 'item ' item
  1524.     if (item ~= 0) then
  1525.       do
  1526.         gv.depth = agenda.item             /* set new depth */
  1527. say 'depth ' gv.depth
  1528.         agenda.next = 0         /* drop it from agenda  */
  1529.         agenda.item = 0         /* and forget its depth */
  1530.         agenda.head = next + 1  /* and advance the head */
  1531.       end
  1532.     else
  1533.       do
  1534.         item = ''
  1535.       end
  1536. say 'AgendaTop ' item
  1537.  
  1538. RETURN item
  1539.  
  1540. /*=======================================================================*/
  1541. /*  AddFileTo                                                            */
  1542. /*     load an array from a file                                         */
  1543. /*=======================================================================*/
  1544.  
  1545. BTAddFileTo: Procedure expose gv. agenda. prune. failed.
  1546.     parse arg array,filename
  1547.  
  1548. say 'AddFileTo ' array filename
  1549. /* performance shouldnt be an issue here */
  1550.  
  1551.     if open(filep, filename, 'Read') then
  1552.       do
  1553.         do while ~eof(filep)
  1554.            buffer = readln(filep)
  1555.            if (buffer ~= '') then
  1556.            select
  1557.              when (array = prune) then
  1558.                 AddToPrune(buffer)
  1559.              when (array = agenda) then
  1560.                 AddToAgenda(buffer)
  1561.              when (Array = failed) then
  1562.                 AddToFailed(buffer)
  1563.              otherwise
  1564.                 nop
  1565.            end  /* select */
  1566.         end /* while */
  1567.         close(filep)
  1568.       end  /* if */
  1569.     else
  1570.       do
  1571.         say 'AddFileTo ' array ' could not read ' filename
  1572.       end  /* else */
  1573.  
  1574. say 'AddFileTo exits'
  1575. RETURN gv.true
  1576.  
  1577. /*=======================================================================*/
  1578. /*  DumpAgendaTofile                                                     */
  1579. /*     unload agenda to a file                                           */
  1580. /*=======================================================================*/
  1581.  
  1582. BTDumpAgendaToFile: Procedure expose gv. agenda.
  1583.     parse arg filename
  1584.  
  1585. say 'DumpAgendaToFile ' filename
  1586. /* note: we deliberately do NOT allow this to effect the global 'depth' */
  1587.  
  1588.     depth = 1
  1589.     if open(filep, filename, 'Write') then
  1590.       do
  1591.         buffer = AgendaTop()
  1592.         do while (buffer ~= '' )
  1593.            writeln(filep, buffer)
  1594.            buffer = AgendaTop()
  1595.         end /* while */
  1596.         agenda.head = 1
  1597.         agenda.tail = 0
  1598.         close(filep)
  1599.       end /* if */
  1600.     else
  1601.       do
  1602.         say 'DumpAgendaToFile could not write to ' filename
  1603.       end /* else */
  1604.  
  1605. say 'DumpAgendaToFile exits ' 
  1606. RETURN gv.true
  1607.  
  1608. /*=======================================================================*/
  1609. /*  DumpFailedToFile                                                     */
  1610. /*     unload failed to a file                                           */
  1611. /*=======================================================================*/
  1612.  
  1613. BTDumpFailedToFile: procedure expose gv. failed.
  1614.     parse arg filename
  1615.  
  1616. say 'DumpFailedToFile ' filename
  1617.     if open(filep, filename, 'Write') then
  1618.       do
  1619.         i = 1
  1620.         buffer = failed.i
  1621.         do while (buffer ~= '0' )
  1622.            writeln(filep, buffer)
  1623.            failed.i = 0
  1624.            failed.buffer = 0
  1625.            i = i + 1
  1626.            buffer = failed.i
  1627.         end /* while */
  1628.         failed.tail = 0
  1629.         close(filep)
  1630.       end /* if */
  1631.     else
  1632.       do
  1633.         say 'DumpFailedToFile could not write to ' filename
  1634.       end /* else */
  1635.  
  1636. say 'DumpFailedToFile exits'
  1637. RETURN gv.true
  1638.  
  1639. /*=======================================================================*/
  1640. /*  End of File 'geturl.rexx'                                            */
  1641. /*=======================================================================*/
  1642.